package com.mezz.bones.im.gateway.boot;

import com.mezz.bones.im.gateway.config.NettyConfig;
import com.mezz.bones.im.gateway.handler.ConnectionLifecycleHandler;
import com.mezz.bones.im.gateway.handler.HeartBeatHandler;
import com.mezz.bones.im.gateway.handler.ImServerHandler;
import com.mezz.bones.im.gateway.handler.NettyGlobalExceptionHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.ByteBufFormat;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * netty服务启动入口
 * 共享统一是如何
 */
@Slf4j
@Service
public abstract class ImServerBoot {

  @Resource
  private HeartBeatHandler heartBeatHandler;

  @Resource
  private ImServerHandler imServerHandler;

  @Resource
  private ConnectionLifecycleHandler connectionLifecycleHandler;

  @Resource
  private EventLoopGroup bossGroup;

  @Resource
  private EventLoopGroup workGroup;

  public void start(NettyConfig nettyConfig) throws Exception {

    Integer port = nettyConfig.getPort();

    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(bossGroup, workGroup);
    serverBootstrap.channel(NioServerSocketChannel.class);
    serverBootstrap.localAddress(port);
//    serverBootstrap.handler(new LoggingHandler(LogLevel.DEBUG));

    //指定在连接建立之前，服务器可连接的最大队列数量。当服务器端接收到新的连接时，如果没有足够的资源及时处理，这些连接将排队等待
    serverBootstrap.option(ChannelOption.SO_BACKLOG, 10240);

    //2h 没数据会发送心跳包检测 TCP协议 保活机制
    serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

    //ChannelOption.TCP_NODELAY 是一个用于设置 TCP 协议是否启用 Nagle 算法的选项。具体来说，
    // 它控制网络套接字是否采用 Nagle 算法来缓冲小的数据包，直到可以发送较大的数据包。
    // 如果将其设置为 true，则会禁用 Nagle 算法，意味着每个小的数据包都会立即发送，而不会等待其他数据合并成一个更大的包。
    serverBootstrap.childOption(ChannelOption.TCP_NODELAY,true);


    serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
      @Override
      protected void initChannel(SocketChannel ch) throws Exception {

        ChannelPipeline pipeline = ch.pipeline();

        //日志handler
        pipeline.addLast(new LoggingHandler(LogLevel.INFO, ByteBufFormat.SIMPLE));

        //连接建立 连接 断开 事件处理
        pipeline.addLast(connectionLifecycleHandler);

        //解码数据包 统一解析成 自定义消息模型 ImMessage ws/tcp自定义协议 子类实现
        decodeMessage(pipeline);

        //心跳handler 先注册IdleStateHandler 如果触发超时条件 会触发userEventTriggered方法 heartBeatHandler负责处理超时逻辑
        pipeline.addLast(new IdleStateHandler(nettyConfig.getReaderIdleTime(), nettyConfig.getWriterIdleTime(), 0, TimeUnit.SECONDS));
        pipeline.addLast(heartBeatHandler);

        //业务处理handler
        pipeline.addLast(imServerHandler);

        //编码数据包
        encodeMessage(pipeline);

        //全局异常处理 需放到最后面
        pipeline.addLast(new NettyGlobalExceptionHandler());

      }
    }); // 设置其他handler

    // 服务器绑定端口监听
    ChannelFuture f = serverBootstrap.bind().sync();
    log.info("Netty 服务端启动成功, 端口:{}",port);
  }


  //子类实现其他的handler功能
  protected void doMessageHandler(ChannelPipeline pipeline){

  }

  protected abstract void encodeMessage(ChannelPipeline pipeline);

  protected abstract void decodeMessage(ChannelPipeline pipeline);

  /**
   * 关闭线程池
   */
  @PreDestroy
  public void close() throws InterruptedException {
    log.info("关闭Netty服务器");
    try {
      if (!bossGroup.isShutdown()) {
        bossGroup.shutdownGracefully();
      }
      if (!workGroup.isShutdown()) {
        workGroup.shutdownGracefully();
      }
    }catch (Exception e){
      log.error("netty close error:{}",e.getMessage());
    }
  }
}
